
package parser

import (
  "scanner"
  "asteval"
  "astexec"
)

type Parser struct {
	look byte
	lexeme string
	scan *scanner.Scanner
}

func (p* Parser) Parse(src string) astexec.Statement {
  p.look = scanner.None
  p.scan = scanner.New(src)
  p.match(scanner.None)
  return p.statement()
}

func (p *Parser) match(tok byte) bool {
	if tok != p.look { return false }
	p.lexeme, p.look = p.scan.Next()
	return true
}

func (p *Parser) factor() *asteval.Expression {
  // number
  if p.look == scanner.Number {
    num := p.lexeme
    p.match(scanner.Number)
    return asteval.Number(num)
  }

  // variable
  if p.look == scanner.Ident {
    nam := p.lexeme
    p.match(scanner.Ident)
    return asteval.Variable(nam)
  }

  // unary operation '-'
  if p.look == scanner.Sub {
    p.match(scanner.Sub)
    ex := p.factor()
    return asteval.Unary('-', ex)
  }

  // expression in parentheses
  if p.look == scanner.LPar {
    p.match(scanner.LPar)
    ex := p.expr()
    p.match(scanner.RPar)
    return ex
  }

  return nil
}

func (p *Parser) term() *asteval.Expression {
  ex1 := p.factor()
  for p.look == scanner.Mul || p.look == scanner.Div {
    op := rune(p.lexeme[0])
    p.match(p.look)
    ex2 := p.factor()
    ex1 = asteval.Binary(op, ex1, ex2)
  }
  return ex1
}

func (p *Parser) expr() *asteval.Expression {
  ex1 := p.term()
  for p.look == scanner.Add || p.look == scanner.Sub {
    op := rune(p.lexeme[0])
    p.match(p.look)
    ex2 := p.term()
    ex1 = asteval.Binary(op, ex1, ex2)
  }
  return ex1
}

func (p *Parser) statement() astexec.Statement {
	switch p.look {
		case scanner.Ident:
			return p.assignment()
		case scanner.Print:
			return p.printexpr()
	}
	return nil
}

func (p *Parser) assignment() astexec.Statement {
	nm := p.lexeme
	p.match(scanner.Ident)
	p.match(scanner.Equal)
	ex := p.expr()
	return astexec.NewAssignment(nm, ex)
}

func (p *Parser) printexpr() astexec.Statement {
	p.match(scanner.Print)
	ex := p.expr()
	return astexec.NewPrint(ex)
}

